// 10.1
const arrowSum = (a, b) => { return a + b };
const normalSum = function(a, b) { return a + b };
const sum = new Function('a', 'b', 'return a + b') // 一般不用

const ints = [2, 4, 6];
ints.map(function(i) { return i * 2 });
ints.map((i) => { return i * 2 });

// good
const double = (x) => { return x * 2 };
const double2 = x => { return x * 2 };
const double3 = x => x * 2;

const getRandomNum = () => { return Math.random() };

const sum = (a, b) => { return a + b };

const setName = (x) => x.name = 'name';
setName({})

// arguments super prototype

// bad
// const sum = a, b => { return a + b }
// const sum = (a, b) => return a + b

// 10.2
// 一个函数对应多个函数名
function sum(a, b) { return a + b };
sum(10, 20)
const anotherSum = sum;
anotherSum(20, 30)
sum = null;
anotherSum(20, 30)

// 函数名
function foo() {}
const bar = () => {}
const baz = function() {}

foo.name
bar.name
baz.name
(() => {}).name
(new Function()).name

foo.bind(this).name // bound foo

const cat = { age: 1, set age(newValue) { this.age = newValue }, get age() { return this.age } }
const p = Object.getOwnPropertyDescriptor(cat, 'age');
p.get.name // get age
p.set.name // set age

// 10.3
// arguments

function hello1(name, age) {
    console.log(`${name} ${age}`);
}

function hello2() {
    console.log(`${arguments[0]} ${arguments[1]}`);
}

// 在箭头函数中使用arguments
function foo() {
    const bar = () => {
        console.log(arguments[0]);
    }
    bar()
}

foo(1)

// 10.4 - js里函数没有重载

// 10.5
function hello (name = 'a') {
    name = 'b';
    return `Hello ${arguments[0]}`
}

hello() // Hello undefined
hello('a'); // Hello b

// arguments 不仅局限于简单的数据类型和引用类型 同样可以是一个函数的返回值

function defaultValue() { return 4 }

function hello (name1 = 'a', name2 = defaultValue()) {

}

function hello () {
    let name1 = 'a';
    let name2 = 'b'
}

// 10.6

const values = [1, 2, 3]
function getSum() {
    let sum = 0;
    for (let i = 0; i < arguments.length; i++) {
        sum += arguments[i]
    }
    return sum;
}

getSum.apply(null, values);
getSum(...values);
getSum(...[0, 1, 2]);

// good
// console.log(sum(10, 20))
// function sum (a ,b) { return a + b }

// bad
console.log(sum(10, 20)) // error
const sum = (a,b) => { return a + b } // let var

// 10.7
// arguments
// ES5 函数内部  arguments this
// ES6 new.target

// arguments.callee -> arguments所在函数的指针

function numberic(num) {
    if (num <= 1) {
        return 1;
    }
    return num * arguments.callee(num - 1);
}

// this

// caller
function outer() { inner() };
function inner() { console.log(inner.caller); } // outer code
// arguments.callee.caller strict error
// arguments.caller strict error
// 函数.caller strict error 不能赋值
outer();

new.target // 检测是不是用new关键字

function ABC() {
    if (!new.target) {
        console.log('unuse new');
    } else {
        console.log('use new');
    }
}

new ABC();
ABC()

const fn = function(arg0, arg1) {} //fn.name -> ''

let sayHi
if (abc) {
    sayHi = function hi() { console.log('hi'); }
} else {
    sayHi = function hi() { console.log('ho'); }
}

function createCompareFunction(prop) {
    return function(obj1, obj2) {
        const v1 = obj1[prop];
        const v2 = obj2[prop];

        // 判断逻辑
        // ...
    }
}

// 10.8 递归
function numberic(num) {
    if (num <= 1) {
        return 1;
    }
    return num * arguments.callee(num - 1);
}

// 命名函数表达式
const numberic = (function f(num) {
    if (num <= 1) {
        return 1;
    }
    return num * f(num - 1)
})

// 10.9 尾调用优化 - 内存优化管理机制
function outer() { return inner() }; // 尾调用
// ES6之前
// 1.outer 栈帧1
// 2.outer -> return 进入inner
// 3.inner 栈帧2
// 4.inner -> return 计算返回值
// 5.返回值推回给outer
// 6.把outer栈帧弹出去

// ES6
// 1.outer 栈帧1
// 2.outer -> return 进入inner
// 3.栈帧1弹出去
// 4.inner 新的栈帧1
// 5.inner -> return 计算返回值
// 6.返回值推回给inner
// 7.把inner栈帧弹出去

// 尾调用优化 - 条件
// 1.严格模式 - arguments/caller
// 2.外部函数返回值 -> 尾调用
// 3.确保尾调用函数返回之后没有后续操作
// 4.尾调用函数不是引用外部函数作用域的自由变量的闭包

// bad case
function outer() { inner() };
function outer() { const a = inner(); return a; }
function outer() { return inner().toString() };
function outer() { const a = '1'; function inner() { return a }; return inner() };

window.a = 'nan';

const obj = {
    a: 'ban',
    getA() {
        return function() {
            return this.a;
        }
    }
}

obj.getA()();

// 思考
window.i = 'i';
const object = {
    i: 'j',
    getI() {
        return this.i;
    }
}

object.getI();
(object.getI)();
(object.getI = object.getI)();